home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Developer Helper 1: Phil & Dave's Excellent CD
/
Excellent CD HFS.raw
/
Moof
/
Goodies
/
MPW Goodies
/
MPW Goodies⁄DTS
/
Srcs
/
Print2 Source
/
Print2.c
next >
Wrap
C/C++ Source or Header
|
1989-04-13
|
24KB
|
778 lines
/* Print2.c
MPW Tool to print two pages per page
Copyright Fred A. Huxham 1988
Additional Options:
-3 Flip page for 3-hole punched paper
This tool supports the same printing options as the standard MPW Print tool
except for the following:
-ff string Form feed string.
-from n Print pages starting from page number n.
-l[ines] n Print (at most) n lines per page.
-ls n Set line spacing.
-n Turn on line numbering.
-nw Width of the line number field in characters.
-page n Number the pages of the file, beginning with n.
-q quality Set print quality on the ImageWriter
-r Output pages to the printer in reverse order.
-title name If printing page headers, use name as the title.
-to n Print pages up to page n.
-tm n Top margin
-bm n Bottom margin
-lm n Left margin
-rm n Right margin
I didn't implement these options for two reasons:
1. I don't use them.
2. I'm lazy.
Revision History:
07/14/88 FAH File created
07/25/88 FAH 1.0A1 All the options that I ever use are complete.
07/26/88 FAH Fixed a bug in DrawBorder where I wasn't checking to
see if the user had selected the option to DrawHeaders or not.
Created a routine, DoAbout so that multiple -p options would
not cause the about string to be printed multiple times.
08/04/88 DJB Various bug fixes, and added 3 Hole option (-3).
09/08/88 DJB Fixed 3 Hole printing.
Fixed overrun of Date/Time, Filename, Page Number in header
Clear and restore FScaleDisable, for MPW 3.0b1
Switched from TN#149 to jamming PrintRecord to set document name
(Added 20% slack feature for Mercer)
09/16/88 DJB Fixed bug in overrun of Date/Time, Filename, Page Number in header
Set minimum font size to 3pt for Phil
*/
#define DEBUG
#include <types.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <ioctl.h>
#include <cursorctl.h>
#include <quickdraw.h>
#include <resources.h>
#include <memory.h>
#include <files.h>
#include <string.h>
#include <strings.h>
#include <errmgr.h>
#include <errors.h>
#include <fonts.h>
#include <printtraps.h>
#include <packages.h>
#include <toolutils.h>
#include <osutils.h>
#include <ctype.h>
/*-------------------------------------------------------------------------------------------------*/
/* Type definitions */
typedef struct Header {
short font;
short size;
short height;
Boolean draw;
Boolean modDate;
char *title;
char date[256];
char time[256];
} Header;
typedef struct Body {
short font;
short size;
short tabWidth;
short lineHeight;
short linesPerPage;
short borderType;
} Body;
/* Globals */
THPrint thePrintRec;
TPPrPort thePrPort;
Handle jobNameH = nil;
Header theHeader;
Body theBody;
short numCopies = 1;
short cmdLineTab = 9999;
short cmdLineFont = 9999;
short cmdLineSize = 0;
Boolean printProgress = false;
Boolean threeHole = false;
Boolean slack = true;
WindowPtr phonyWindow = nil;
/* Enumeration constants */
enum sides {left,right};
/* Defines */
#define maxStr 512
/* Function prototypes */
void SetDefaults();
void DoAbout();
void SetFileStuff(char *fileName);
Rect GetrPage(THPrint thePrintRec);
void ForceLandscape(THPrint thePrintRec);
void DrawHeader(Rect *theRect,short pageNum);
Rect DrawBorder(short whichSide,short pageNum);
short Tabs2Spaces(char *theString);
OSErr PrintFile(FILE *theStream);
int SetOptions(int argc, char *argv[],short *files);
void PrValidateAndName(THPrint tPR, char *fname);
int main(int argc, char *argv[]);
/*-------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------*/
/* Set the default values for the globals */
void SetDefaults()
{
theHeader.font = times;
theHeader.size = 8;
theHeader.height = 0;
theHeader.draw = false;
theBody.font = courier;
theBody.size = 6;
theBody.tabWidth = 4;
theBody.borderType = 0;
}
/*-------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------*/
/* Prints the about string to standard out (for the -p option) */
void DoAbout()
{
char *about = "\nPrint2 (Version 1.0B1 September 8, 1988) - (c) 1988 Fred A. Huxham\n"
" (Modifications and minor bug fixes by - Dave Burnard)\n";
fprintf(stdout,about);
fflush(stdout);
}
/*-------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------*/
/* This routine does a few different things. First, set the header title to the
filename. Then set the header date to either the file's last modification date/time
or the current date/time, depending on the theHeader.modDate flag. Next, set the
font, size and tab values for the print job according to the following,
1. Get the values from the command line.
2. Get the values from the resource fork of the file (only if MPW text files)
3. Use the defaults.
*/
void SetFileStuff(char *fileName)
{
unsigned long temp, result;
long temp1;
ParamBlockRec thePB;
theHeader.title = fileName;
if(theHeader.modDate) {
thePB.fileParam.ioCompletion = 0;
thePB.fileParam.ioNamePtr = c2pstr(fileName);
thePB.fileParam.ioVRefNum = 0;
thePB.fileParam.ioFVersNum = 0;
thePB.fileParam.ioFDirIndex = 0;
if(!(PBGetFInfo(&thePB,false))) temp = thePB.fileParam.ioFlMdDat;
p2cstr(fileName);
} else GetDateTime(&temp);
IUDateString(temp,shortDate,theHeader.date);
IUTimeString(temp,false,theHeader.time);
result = faccess(fileName,F_GFONTINFO,&temp1);
if(cmdLineFont != 9999) theBody.font = cmdLineFont;
else if(!result) theBody.font = HiWord(temp1);
if(cmdLineSize) theBody.size = cmdLineSize;
else if(!result >= 0) theBody.size = LoWord(temp1);
if(cmdLineTab != 9999) theBody.tabWidth = cmdLineTab;
else if(!(faccess(fileName,F_GTABINFO,&temp1))) theBody.tabWidth = temp1;
}
/*-------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------*/
/* Calculate and set various values having to do with lineHeight, linesPerPage, header height,
etc... */
#define GetFScaleDisable() (*((char *) 0x0A63))
void SetLineStuff()
{
FontInfo fInfo;
short pageHeight;
Rect tempRect;
Boolean fscaledisable;
if(theHeader.draw) {
TextFont(theHeader.font);
TextSize(theHeader.size);
GetFontInfo(&fInfo);
theHeader.height = fInfo.ascent + fInfo.descent + fInfo.leading + 4;
}
fscaledisable = GetFScaleDisable(); /* Get state of font scaling flag */
SetFScaleDisable(false); /* Enable font scaling so we get scaled lineheight */
TextFont(theBody.font);
TextSize(theBody.size);
GetFontInfo(&fInfo);
theBody.lineHeight = fInfo.ascent + fInfo.descent + fInfo.leading;
SetFScaleDisable(fscaledisable); /* Return to previous state of font scaling flag */
tempRect = GetrPage(thePrintRec);
pageHeight = tempRect.bottom - tempRect.top - theHeader.height;
theBody.linesPerPage = pageHeight/theBody.lineHeight;
}
/*-------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------*/
/* Returns the rPage rectangle out of the print record. */
Rect GetrPage(THPrint thePrintRec)
{
Rect tempR;
tempR = (**thePrintRec).prInfo.rPage;
if (threeHole) tempR.top += 15;
return (tempR);
}
/*-------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------*/
/* This function takes the print record and forces it into landscape mode */
/* This is sicker than sick. It depends on the values of completely undocumented bits
of the print record. This routine is subject to break at any point in time. */
void ForceLandscape(THPrint thePrintRec)
{
Rect tempRect;
/* If the portrait bit of the wDev field of the print record is set (bit 1), then
rotate the rPaper and rPage rectangles by 90 degrees and finally unset the
portrait bit to fake the print driver into thinking that the user has selected
landscape mode. At the current time (July 1988) both the ImageWriter and
LaserWriter use the same bit of the wDev for a portrait/landscape flag.
As long as this stays the same, this routine will work for both types of printers. */
if((**thePrintRec).prStl.wDev & 0x0002) {
tempRect.top = (**thePrintRec).rPaper.left;
tempRect.left = (**thePrintRec).rPaper.top;
tempRect.bottom = (**thePrintRec).rPaper.right;
tempRect.right = (**thePrintRec).rPaper.bottom;
(**thePrintRec).rPaper = tempRect;
tempRect.bottom = (**thePrintRec).prInfo.rPage.bottom;
(**thePrintRec).prInfo.rPage.bottom = (**thePrintRec).prInfo.rPage.right;
(**thePrintRec).prInfo.rPage.right = tempRect.bottom;
(**thePrintRec).prInfoPT.rPage = (**thePrintRec).prInfo.rPage;
(**thePrintRec).prStl.wDev ^= 0x0002;
}
/*
** Another completely skanky trick:
**
** If 3-hole paper is used we need to rotate the paper 180 degrees
** this is accomplished by setting the horizontal flip and vertical flip
** bits in the printX[5] field of the PrintRecord
*/
if (threeHole) {
(**thePrintRec).printX[5] |= 0x0006;
}
}
/*-------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------*/
/* Draw the page headers */
void DrawHeader(Rect *theRect,short pageNum)
{
short oldFont,oldSize,titleWidth,dateWidth,pageWidth,pageCenter;
char thePageStr[256];
/* Save the current font and size settings */
oldFont = thePrPort->gPort.txFont;
oldSize = thePrPort->gPort.txSize;
/* Set the header font and size and then draw the date/time value left justified */
TextFont(theHeader.font);
TextSize(theHeader.size);
MoveTo(theRect->left+4,theRect->top + theHeader.height-4);
DrawString(theHeader.date);
drawstring(" ");
DrawString(theHeader.time);
dateWidth = StringWidth(theHeader.date) + StringWidth(theHeader.time) + stringwidth(" ");
/* Draw the page number right justified */
NumToString((long)pageNum,thePageStr);
pageWidth = StringWidth(thePageStr) + StringWidth("\pPage ");
MoveTo(theRect->right - 4 - pageWidth, theRect->top + theHeader.height-4);
DrawString("\pPage ");
DrawString(thePageStr);
/* Draw the title centered over the document */
titleWidth = stringwidth(theHeader.title);
pageCenter = ((theRect->right - pageWidth - 4) + (theRect->left + dateWidth + 4))/2;
if (titleWidth + pageWidth + dateWidth > theRect->right - theRect->left - 16)
{
char tmp_str[256];
char *first_colon, *last_colon;
first_colon = strchr(theHeader.title, ':');
last_colon = strrchr(theHeader.title, ':');
if (first_colon != nil && first_colon != last_colon) {
strncpy(tmp_str, theHeader.title, first_colon - theHeader.title + 1);
tmp_str[first_colon - theHeader.title + 1] = '\0';
strcat(tmp_str, " ... ");
strcat(tmp_str, last_colon);
strcpy(theHeader.title, tmp_str);
titleWidth = stringwidth(theHeader.title);
}
}
MoveTo(pageCenter - (titleWidth>>1),theRect->top + theHeader.height-4);
drawstring(theHeader.title);
/* Restore the old font and size settings */
TextFont(oldFont);
TextSize(oldSize);
}
/*-------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------*/
/* Draw the page border. This routine draw either the right or left page border on the paper.
The border styles are the same as the standard MPW print tool. Within this routine we also
draw the page header (if that option has been specified) */
Rect DrawBorder(short whichSide,short pageNum)
{
Rect tempRect;
tempRect = GetrPage(thePrintRec);
ClipRect(&tempRect);
if(whichSide == left) tempRect.right = ((tempRect.right-tempRect.left) >> 1) - 1;
if(whichSide == right) tempRect.left = ((tempRect.right-tempRect.left) >> 1) + 1;
tempRect.bottom -= 1;
if(theHeader.draw) DrawHeader(&tempRect,pageNum);
switch(theBody.borderType) {
case 0:
if(theHeader.draw) tempRect.top += theHeader.height;
break;
case 1:
FrameRoundRect(&tempRect,10,10);
if(theHeader.draw) {
MoveTo(tempRect.left,tempRect.top+theHeader.height);
LineTo(tempRect.right-1,tempRect.top+theHeader.height);
tempRect.top += theHeader.height;
}
break;
case 2:
if(theHeader.draw) tempRect.top += theHeader.height;
FrameRoundRect(&tempRect,10,10);
break;
}
return(tempRect);
}
/*-------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------*/
/* This routine takes a C string, and then goes through it character by character looking for
tab characters (0x09). When it finds one, it figures out how many space characters (0x20)
should be inserted for that tab. Two strings are used in this routine, the original, and
a temporary one, into which the original, and the substituted space characters are copied.
When the end of the original string is reached, the temp string is copied over the original. */
short Tabs2Spaces(char *theString)
{
char tempStr[maxStr];
char *inStr,*outStr;
short i,chars,spacesToInsert;
inStr = theString;
outStr = tempStr;
chars = 0;
while(*inStr != 0x00) {
if(*inStr == 0x09) {
inStr++;
spacesToInsert = theBody.tabWidth - (chars % theBody.tabWidth);
chars += spacesToInsert;
for(i=1;i<=spacesToInsert;i++) *outStr++ = 0x20;
} else {
*outStr++ = *inStr++;
chars++;
}
}
*outStr = 0x00;
strcpy(theString,tempStr);
return(chars);
}
/*-------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------*/
/* This routine prints the specified file. */
OSErr PrintFile(FILE *theStream)
{
Rect drawRect;
short pageSide,i;
char tempStr[maxStr];
Boolean done = false;
OSErr result = noErr;
short pageNum = 0;
long totalChars = 0;
long totalLines = 0;
/* Open a printing grafPort */
thePrPort = PrOpenDoc(thePrintRec,nil,nil);
if(!(result = PrError())) {
SetLineStuff();
TextFont(theBody.font);
TextSize(theBody.size);
/* While we haven't reached the end of the file... */
while(!done) {
PrOpenPage(thePrPort,nil);
if(!(result = PrError())) {
/* Draw the left and right side of each piece of paper */
for(pageSide=left;pageSide<=right && !done;pageSide++) {
SpinCursor(1);
drawRect = DrawBorder(pageSide,++pageNum);
ClipRect(&drawRect);
for(i=1;i<theBody.linesPerPage && !done;i++) {
MoveTo(drawRect.left+3,drawRect.top+(i*theBody.lineHeight));
if(fgets(tempStr,maxStr,theStream)) {
totalChars += Tabs2Spaces(tempStr);
totalLines ++;
drawstring(tempStr);
}
else done = true;
}
}
}
PrClosePage(thePrPort);
}
} else {
fprintf(stderr,"### Print2 - PrOpenDoc fails, you lose.\n");
result = 3;
return (result);
}
PrCloseDoc(thePrPort);
if(printProgress) {
fprintf(stdout,"Printing \"%s\"... %d characters; %d lines; %d page(s) \n",
theHeader.title,totalChars,totalLines,pageNum);
fflush(stdout);
}
return(result);
}
/*-------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------*/
/* This routine takes argc and argv and sets all the options specified on the command line. */
int SetOptions(int argc, char *argv[],short *files)
{
short argNum,stemp;
long length;
int status,temp;
char *badOpt = "### %s - \"%s\" is not a valid option\n";
char *badVal = "### %s - Usage error: %s %s (invalid value)\n";
for(argNum=1;argNum<argc;argNum++) {
length = strlen(argv[argNum]);
if(*argv[argNum] != '-') argv[++(*files)] = argv[argNum];
else switch(tolower(*(argv[argNum]+1))) {
/* Three hole paper? */
case '3':
if(length == 2) threeHole = true;
else {
fprintf(stderr,badOpt,argv[0],argv[argNum]);
status = 1;
}
break;
/* Border style */
case 'b':
if(length == 2) theBody.borderType = 1;
else if(length == 3 && (*(argv[argNum]+2)) == '2') theBody.borderType = 2;
else {
fprintf(stderr,badOpt,argv[0],argv[argNum]);
status = 1;
}
break;
/* Number of copies */
case 'c':
if(length == 2 || (length == 7 && !(strcmp(argv[argNum],"-copies")))) {
temp = atol(argv[++argNum]);
if(temp >= 1 && temp <= 100) numCopies = temp;
else {
fprintf(stderr,badVal,argv[0],argv[0],argv[argNum-1]);
status = 1;
}
} else {
fprintf(stderr,badOpt,argv[0],argv[argNum]);
status = 1;
}
break;
/* Font to use */
case 'f':
if(length == 2 || (length == 5 && !(strcmp(argv[argNum],"-font")))) {
getfnum(argv[++argNum],&cmdLineFont);
if(!cmdLineFont) {
fprintf(stderr,badVal,argv[0],argv[0],argv[argNum-1]);
status = 1;
}
} else {
fprintf(stderr,badOpt,argv[0],argv[argNum]);
status = 1;
}
break;
/* Print headers (-h), header font (-hf[ont]), and header size (-hs[ize]) */
case 'h':
if(length == 2) theHeader.draw = true;
else
if((length == 3 && (*(argv[argNum]+2)) == 'f') ||
(length == 6 && !(strcmp(argv[argNum],"-hfont")))) {
getfnum(argv[++argNum],&stemp);
if(stemp) theHeader.font = stemp;
else {
fprintf(stderr,badVal,argv[0],argv[0],argv[argNum-1]);
status = 1;
}
}
else
if((length == 3 && (*(argv[argNum]+2)) == 's') ||
(length == 6 && !(strcmp(argv[argNum],"-hsize")))) {
temp = atol(argv[++argNum]);
if(temp > 2 && temp < 37) theHeader.size = temp;
else {
fprintf(stderr,badVal,argv[0],argv[0],argv[argNum-1]);
status = 1;
}
} else {
fprintf(stderr,badOpt,argv[0],argv[argNum]);
status = 1;
}
break;
/* Use modification date in header (rather than current date) */
case 'm':
if(length == 3 && (*(argv[argNum]+2)) == 'd') theHeader.modDate = true;
else {
fprintf(stderr,badOpt,argv[0],argv[argNum]);
status = 1;
}
break;
/* Allow the "slacker" to redeem himself */
case 'n':
if(length == 8 && !(strcmp(argv[argNum],"-noslack"))) {
slack = false;
} else {
fprintf(stderr,badOpt,argv[0],argv[argNum]);
status = 1;
}
break;
/* Write progress information */
case 'p':
if(length == 2) {
printProgress = true;
} else {
fprintf(stderr,badOpt,argv[0],argv[argNum]);
status = 1;
}
break;
/* Set the body font size */
case 's':
if(length == 2 || (length == 5 && !(strcmp(argv[argNum],"-size")))) {
temp = atol(argv[++argNum]);
if(temp > 2 && temp < 37) cmdLineSize = temp;
else {
fprintf(stderr,badVal,argv[0],argv[0],argv[argNum-1]);
status = 1;
}
} else {
fprintf(stderr,badOpt,argv[0],argv[argNum]);
status = 1;
}
break;
/* Set the tab width */
case 't':
if(length == 2 || (length == 5 && !(strcmp(argv[argNum],"-tabs")))) {
temp = atol(argv[++argNum]);
if(temp >= 0 && temp <= 16) cmdLineTab = temp;
else {
fprintf(stderr,badVal,argv[0],argv[0],argv[argNum-1]);
status = 1;
}
} else {
fprintf(stderr,badOpt,argv[0],argv[argNum]);
status = 1;
}
break;
/* A bad (non-supported) option */
default:
fprintf(stderr,badOpt,argv[0],argv[argNum]);
status = 1;
break;
}
}
return(status);
}
/*
** Skank to get print manager to use document name (TN #149)
*/
void
PrValidateAndName(THPrint tPR, char *fname)
{
#if 0
Rect r;
if (phonyWindow != nil) DisposeWindow(phonyWindow);
phonyWindow = nil;
SetRect(&r, 32000, 32000, 32010, 32010); /* Pretty far off-screen */
phonyWindow = newwindow(nil, &r, fname, true, plainDBox, (WindowPtr) -1, false, nil);
PrValidate(tPR);
if (phonyWindow != nil) DisposeWindow(phonyWindow);
phonyWindow = nil;
#else
char *username, *jobName;
char tmp[256];
/*
** Special treatment for the slacker (once in a while...(20%))
*/
username = getenv("User");
if (slack && username != nil && !strcmp(username, "Paul Mercer") && (TickCount()%5 == 0) ) {
strcpy(tmp, "SLACKER! ");
strcat(tmp, fname);
} else {
strcpy(tmp, fname);
}
c2pstr(tmp);
jobName = tmp;
if (jobNameH != nil) DisposHandle(jobNameH);
/*
** Another completely skanky trick:
**
** Jam the PrintRecord with our jobName
** this is accomplished by placing a handle to the
** name in the printX[3] and [4] fields of the PrintRecord
**
** This is sicker than sick. It depends on the values of
** completely undocumented bits of the print record.
**
** This routine is subject to break at any point in time.
**
** If anything fails, just fall through
*/
jobNameH = NewString(jobName);
if (jobNameH != nil) {
*((Handle *) &(*tPR)->printX[3]) = jobNameH;
}
PrValidate(tPR);
#endif
}
/*-------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------*/
/* The main routine of the tool. */
int main(int argc, char *argv[])
{
short argNum,files,i;
FILE *theStream;
char errorBuffer[256];
int status;
files = 0;
InitGraf(&qd.thePort);
InitCursorCtl(nil);
SetDefaults();
status = SetOptions(argc,argv,&files);
if(printProgress) DoAbout();
if(!(thePrintRec = (THPrint)NewHandle(sizeof(TPrint)))) {
fprintf(stderr,"### %s - Unable to allocate a print record \n", argv[0]);
fprintf(stderr,"# %s\n", GetSysErrText(MacOSErr,errorBuffer));
status = 3;
}
else {
PrOpen();
if(PrError() != 0) {
fprintf(stderr,"### %s - PrOpen fails, check that a printer is selected.\n", argv[0]);
DisposHandle((Handle) thePrintRec);
status = 3;
return (status);
}
if(files == 0) {
if(ioctl(fileno(stdin),FIOINTERACTIVE,nil))
for(i=1;i<=numCopies;i++) {
PrintDefault(thePrintRec);
ForceLandscape(thePrintRec);
/*
** Validate and Set Document Name
*/
PrValidateAndName(thePrintRec, "Dev:Stdin");
PrintFile(stdin);
}
else {
fprintf(stderr,"### %s - Cannot print input from the console\n", argv[0]);
status = 2;
}
} else {
for(argNum=1;argNum<=files;argNum++) {
if(theStream = fopen(argv[argNum],"r")) {
PrintDefault(thePrintRec);
SetFileStuff(argv[argNum]);
ForceLandscape(thePrintRec);
/*
** Validate and Set Document Name
*/
PrValidateAndName(thePrintRec, argv[argNum]);
for(i=1;i<=numCopies;i++) PrintFile(theStream);
fclose(theStream);
} else {
fprintf(stderr,"### %s - Unable to open the file: \"%s\"\n", argv[0], argv[argNum]);
fprintf(stderr,"# %s\n", GetSysErrText(MacOSErr,errorBuffer));
status = 2;
}
}
}
PrClose();
DisposHandle((Handle) thePrintRec);
}
return(status);
}
/*-------------------------------------------------------------------------------------------------*/